package com.cefetmg.mmc.mrcpsp.process.impl;

import org.apache.log4j.Logger;

import com.cefetmg.mmc.mrcpsp.model.enums.EnumLogUtils;
import com.cefetmg.mmc.mrcpsp.model.main.Project;
import com.cefetmg.mmc.mrcpsp.process.InstanceDataProcessor;
import com.cefetmg.mmc.mrcpsp.process.JobTimeProcessor;
import com.cefetmg.mmc.mrcpsp.process.JobsModeInformationProcessor;
import com.cefetmg.mmc.mrcpsp.process.JobsModeSelectProcessor;
import com.cefetmg.mmc.mrcpsp.process.MmProcessor;
import com.cefetmg.mmc.mrcpsp.process.RestrictionsProcessor;
import com.cefetmg.mmc.mrcpsp.process.ResultsProcessor;
import com.cefetmg.mmc.mrcpsp.process.initialsolution.GenerateInitialSolutionGRASP;
import com.cefetmg.mmc.mrcpsp.process.initialsolution.impl.GenerateInitialSolutionGRASPImpl;
import com.cefetmg.mmc.mrcpsp.process.job.JobPriorityRulesOperations;
import com.cefetmg.mmc.mrcpsp.process.job.impl.JobPriorityRulesOperationsImpl;
import com.cefetmg.mmc.mrcpsp.utils.LogUtils;

/**
 * @author mrcosta
 *
 */
public class MmProcessorImpl implements MmProcessor {
	
	private static final Logger log = Logger.getLogger(MmProcessorImpl.class);
	
	private InstanceDataProcessor instanceDataProcessor;	
	private JobsModeSelectProcessor jobsModeSelectProcessor;
	private JobPriorityRulesOperations	jobPriorityRulesOperations;
	private GenerateInitialSolutionGRASP generateInitialSolutionGRASP;
	private JobsModeInformationProcessor jobsModeInformationProcessor;
	private RestrictionsProcessor restrictionsProcessor;
	private JobTimeProcessor jobTimeProcessor;
	private ResultsProcessor resultsProcessor;
	private Project project;	
	
	private boolean success;
	
	
	public MmProcessorImpl() {		
		init();
	}
	
	private void init() {
		log.info("Starting the execution . . .");		
		
		success = true;
	}
	
	private boolean executeGetInstanceData(String fileName) {
		if (success) {			
			try {	
				log.info("==========================================================================");
				log.info("Loading instance's data . . .");
				instanceDataProcessor = new InstanceDataProcessorImpl();
				project = instanceDataProcessor.getInstanceData(fileName);
				success = true;
				log.info("Loading instance's data . . . DONE \n");	
			} catch (Exception e) {
				log.error(e.toString() + " --- " + e.getMessage());
				success = false;
			}			
		}		
			
		return success;
	}
	
	private boolean executeGetJobModesInformation() {
		if (success) {			
			try {	
				log.info("==========================================================================");
				log.info("Getting job modes information . . .");
				jobsModeInformationProcessor = new JobsModeInformationProcessorImpl();
				project.setJobs(jobsModeInformationProcessor.getJobModesInformation(project.getJobs()));
				success = true;
				log.info("Getting job modes information . . . DONE \n");	
			} catch (Exception e) {
				log.error(e.toString() + " --- " + e.getMessage());
				success = false;
			}			
		}
			
		return success;
	}
	
	private boolean executeJobsPriorityRules() {
		if (success) {			
			try {
				log.info("==========================================================================");
				log.info("Executing Job Priority Rules . . .");
				
				jobPriorityRulesOperations = new JobPriorityRulesOperationsImpl();
				project.setJobs(jobPriorityRulesOperations.setJobsPriorityRuleInformation(project.getJobs()));
				success = true;
				log.info("Executing Job Priority Rules . . .DONE \n");	
			} catch (Exception e) {
				log.error(e.toString() + " --- " + e.getMessage());
				success = false;
			}			
		}
			
		return success;
	}
	
	private boolean executeGenerateInitialSolution() {
		if (success) {			
			try {
				log.info("==========================================================================");
				log.info("Generating the Initial Solution with GRASP . . .");
				
				generateInitialSolutionGRASP = new GenerateInitialSolutionGRASPImpl();
				project.setStaggeredJobs(generateInitialSolutionGRASP.getInitialSolution(project));
				
				log.info(LogUtils.generateJobsIDListLog(project.getJobs(), EnumLogUtils.LIST_JOBS));
				log.info(LogUtils.generateJobsIDListLog(project.getStaggeredJobs(), EnumLogUtils.STAGGERED_JOBS));
				
				success = true;
				log.info("Generating the Initial Solution with GRASP . . .DONE \n");
			} catch (Exception e) {
				log.error(e.toString() + " --- " + e.getMessage());
				success = false;
			}			
		}
				
		return success;
	}
	
	private boolean executeJobsModeSelect() {
		if (success) {			
			try {	
				log.info("==========================================================================");
				log.info("Executing jobs mode select . . .");
				
				jobsModeSelectProcessor = new JobsModeSelectProcessorImpl();
				project.setStaggeredJobs(jobsModeSelectProcessor.getModeToStaggeredJobs(project.getStaggeredJobs()));
				success = true;
				log.info("Executing jobs mode select . . .DONE \n");
			} catch (Exception e) {
				log.error(e.toString() + " --- " + e.getMessage());
				return success = false;
			}			
		}	
				
		return success;
	}
	
	private boolean executeCheckRestrictions() {
		if (success) {			
			try {	
				log.info("==========================================================================");
				log.info("Executing Restrictions Verification . . .");
				
				restrictionsProcessor = new RestrictionsProcessorImpl();
				success = restrictionsProcessor.checkNonRenewableResourcesAmount(project);
				log.info("Executing Restrictions Verification . . .DONE \n");	
			} catch (Exception e) {
				log.error(e.toString() + " --- " + e.getMessage());
				return success = false;
			}			
		}	
		
		return success;
	}
	
	private boolean executeGetJobTimes() {
		if (success) {			
			try {	
				log.info("==========================================================================");
				log.info("Getting Initial and Finish Time for Jobs . . .");
				
				jobTimeProcessor = new JobTimeProcessorImpl();
				success = jobTimeProcessor.getJobTimes(project.getResourceAvailabilities(), project.getStaggeredJobs());
				log.info("Getting Initial and Finish Time for Jobs . . .DONE \n");	
			} catch (Exception e) {
				log.error(e.toString() + " --- " + e.getMessage());
				return success = false;
			}			
		}	
			
		return success;
	}
	
	private void executeWriteResults() {		
		try {	
			log.info("==========================================================================");
			log.info("Writing the results. . .");
			
			resultsProcessor = new ResultsProcessorImpl();
			
			resultsProcessor.getMakespanFromScheduledJobs(project, success);
			resultsProcessor.checkExecutionTypeToGenerateResults(project);
			log.info("FILE: " + project.getFileName() + " - MAKESPAN: " + project.getMakespan());
						
			log.info("Writing the results . . .DONE \n");
		} catch (Exception e) {
			log.error(e.toString() + " --- " + e.getMessage());				
		}
	}
	
	@Override
	public Integer execute(String fileName) {
		success = true;
		
		// reading the file information and creating the objects
		success = executeGetInstanceData(fileName);
		
		// getting some useful information about the modes of each job - priority rules will use this information
		success = executeGetJobModesInformation();
		
		// some priority rules can run without runtime information update (like NIS)
		success = executeJobsPriorityRules();
		
		// generating the initial solution
		success = executeGenerateInitialSolution();
		
		// getting a mode for each job
		success = executeJobsModeSelect();
		
		// checking if the restrictions are OK
		success = executeCheckRestrictions();
		
		// getting initial and finish time for jobs
		success = executeGetJobTimes();
		
		// writing the results
		executeWriteResults();
		
		return project.getMakespan();
	}
	
	

}
